package com.tools.service.impl;

import com.tools.commons.cache.AbstractStringCacheStore;
import com.tools.commons.exceptions.BadRequestException;
import com.tools.commons.utils.CodeGeneratorUtil;
import com.tools.commons.utils.IdGenerator;
import com.tools.config.SystemProperties;
import com.tools.model.entity.UserEntity;
import com.tools.model.params.UserParams;
import com.tools.security.base.AuthToken;
import com.tools.security.context.SecurityContext;
import com.tools.security.context.SecurityContextHolder;
import com.tools.security.context.UserInfo;
import com.tools.security.utils.SecurityCacheKeyUtils;
import com.tools.service.AuthService;
import com.tools.service.CaptchaService;
import com.tools.service.UserInfoService;
import com.tools.service.UserService;
import com.tools.utils.SendSms;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.SystemPropertyUtils;

import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author wangminggen
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {
    private static final int ACCESS_TOKEN_EXPIRED_SECONDS = 60 * 30 * 24 * 365;
    private static final int REFRESH_TOKEN_EXPIRED_DAYS = 1;
    private static final String codePrefix = "code::";
    private static final String codeSendCountPrefix = "code::use::";

    private final CaptchaService captchaService;
    private final AbstractStringCacheStore cacheStore;
    private final UserService userService;
    private final UserInfoService userInfoService;
    private final SystemProperties systemProperties;

    @Override
    public AuthToken login(UserParams loginParams) {
        String phone = loginParams.getPhone();
        UserEntity user = userService.loginInPhone(phone, loginParams.getRole().equals(UserParams.Role.DOCTOR));
        if (user != null) {
            //暂时不校验验证码，验证码只要为手机号部分字段即可
            Optional<String> s = cacheStore.get(codePrefix + phone);
            cacheStore.delete(codePrefix + phone);
            if (s.map(a -> a.equals(loginParams.getCode())).orElse(false)) {
                return buildAuthToken(user);
            }

        }
        throw new BadRequestException("登陆失败");
    }

    public AuthToken buildAuthToken(@NonNull UserEntity user) {
        Assert.notNull(user, "User must not be null");
        AuthToken token = new AuthToken();

        token.setAccessToken(IdGenerator.uuid());
        token.setDoctor(user.isDoctor());
        token.setInfo(userInfoService.queryInfo(user));

        cacheStore.putAny(SecurityCacheKeyUtils.buildUserIdAccessTokenKey(user.getUserId()), token.getAccessToken(), ACCESS_TOKEN_EXPIRED_SECONDS, TimeUnit.SECONDS);
        cacheStore.putAny(SecurityCacheKeyUtils.buildAccessTokenUserIdKey(token.getAccessToken()), user.getUserId(), ACCESS_TOKEN_EXPIRED_SECONDS, TimeUnit.SECONDS);
        return token;
    }

    @Override
    public void logout() {
        SecurityContext context = SecurityContextHolder.getContext();
        if (!context.isLogin()) {
            throw new BadRequestException("您尚未登录，因此无法注销");
        }
        UserInfo userInfo = context.getUserInfo();
        String userId = userInfo.getUserId();
        cacheStore.getAny(SecurityCacheKeyUtils.buildUserIdAccessTokenKey(userId), String.class)
                .ifPresent(accessToken -> cacheStore.delete(SecurityCacheKeyUtils.buildAccessTokenUserIdKey(accessToken)));

        cacheStore.delete(SecurityCacheKeyUtils.buildUserIdAccessTokenKey(userId));
    }

    @Override
    public void sendCode(String phone) {
        String key = codeSendCountPrefix + phone;
        int count = cacheStore.getAny(key, Integer.class).orElse(0);
        if (count > systemProperties.getSmsSendCountLimit()) {
            throw new BadRequestException("发送频繁,5分钟后再试");
        }
        String code = "123456";

        //真发
        if (systemProperties.isSmsEnable()) {
            code = CodeGeneratorUtil.randCode(6);
            SendSms.sendCode(code, phone);
        }

        cacheStore.putAny(key, count + 1, 5, TimeUnit.MINUTES);
        log.info("phone => {}, code => {}", phone, code);
        cacheStore.putAny(codePrefix + phone, code);
    }
}
